ICTSC2019 一次予選 問題解説: コンテナが作れない

問題文

あなたはICTSC専門学校の3学生です。 新入生向けに学内向けCTFを開催するにあたってアプリ開発チームとサーバ構築チームに分かれてCTFサーバの構築を行うことになりました。サーバ構築チームはあなたの後輩のA君が担当、あなたは両方を担当しています。当初はサークルで余っていたラックサーバ上のVMで展開する予定でしたがA君が突然「VMじゃなくてDockerでやりたいです」と言い出しました。 A君は最近Dockerを触り始めたばかりなのですが、サーバの構成自体は簡単なので大丈夫だろうと判断し任せることになりました。 アプリ開発が終わり、A君に展開に必要なファイルを渡してDocker上で展開ししてもらったところコンパイルがうまくいかないと言われました。 これ以上時間をかけてしまうと後のスケジュールに影響してしまうため、A君に代わりあなたがトラブルシュートを行うことにします。 原因を特定しスコアボードが正常に表示されるようにDockerfileを修正してください。

条件

  • スコアボードアプリケーションのソースコード及びdocker-compose.yamlを変更してはいけない
  • MySQL及びRedisの構成ファイルを変更してはいけない
  • 必ずアプリケーションのコンパイル・実行はコンテナ上で行い、原則ホストOSでパッケージをインストールしてはいけない
  • コンテナで使用しているイメージは変更してはいけない
  • 3つのDockerfileから構成されるようにすること

ゴール

問題の原因を特定して http://192.168.0.1/index にアクセスしたときスコアボードが正常に表示されるように修正する。

情報

問題サーバー

  • IPアドレス: 192.168.0.1
  • ユーザー: admin
  • パスワード: 248b1fa8f258

解説

踏み台サーバを経由し問題サーバ上でdocker-compose buildしてみると以下のようなエラーで終了することがわかります。

... Step 4/8 : RUN go build -tags netgo -o webapp main.go ---> Running in 566031a5ac14 main.go:9:2: cannot find package "github.com/garyburd/redigo/redis" in any of: /usr/local/go/src/github.com/garyburd/redigo/redis (from $GOROOT) /go/src/github.com/garyburd/redigo/redis (from $GOPATH) main.go:4:2: cannot find package "github.com/gin-gonic/gin" in any of: /usr/local/go/src/github.com/gin-gonic/gin (from $GOROOT) /go/src/github.com/gin-gonic/gin (from $GOPATH) main.go:8:2: cannot find package "github.com/go-gorp/gorp" in any of: /usr/local/go/src/github.com/go-gorp/gorp (from $GOROOT) /go/src/github.com/go-gorp/gorp (from $GOPATH) main.go:10:2: cannot find package "github.com/go-sql-driver/mysql" in any of: /usr/local/go/src/github.com/go-sql-driver/mysql (from $GOROOT) /go/src/github.com/go-sql-driver/mysql (from $GOPATH) ERROR: Service 'webapp' failed to build: The command '/bin/sh -c go build -tags netgo -o webapp main.go' returned a non-zero code: 1

このことから、アプリケーションのビルドでエラーが発生しその原因は必要なパッケージが存在していないということがわかります。 この問題の解決法はいくつかあるためあくまで一例ですが以下のようにwebapp/Dockerfileを修正することで解決できます。

FROM golang:alpine ADD ./Score /work WORKDIR /work RUN apk -U add git RUN go get -u github.com/go-sql-driver/mysql && \ go get -u github.com/go-gorp/gorp && \ go get -u github.com/gin-gonic/gin && \ go get -u github.com/garyburd/redigo/redis && \ go build -tags netgo -o webapp main.go FROM busybox ENV GOPATH=/usr/local/bin/ COPY --from=builder /work/exec-webapp.sh /usr/local/bin/ ENTRYPOINT ["/usr/local/bin/exec-webapp.sh", "/usr/local/bin/webapp"]

これでアプリケーションのビルドは成功しましたが、
... Removing intermediate container c07108921a48 ---> ac82f0d3b17a Step 8/9 : COPY --from=builder /work/exec-webapp.sh /usr/local/bin/ ERROR: Service 'webapp' failed to build: invalid from flag value builder: pull access denied for builder, repository does not exist or may require 'docker login`
とあるように、COPY命令でビルドしたアプリケーションをbuilderというコンテナからコピーしようとしてもコンテナが見つからず失敗してしまいます。
このbuilderというコンテナは本来先ほどアプリケーションを指しているべきなのですが、その設定が抜けていたために起きていました。
このような構成になっているのはDocker MultiStage Buildという技術を利用しているためで、こうすることで一つのDockerfileでビルドと実行を行うコンテナを同時に展開することができ、実際に実行するコンテナサイズを最小限に抑えることができます。
よって、このエラーを解決するためにはAS builderをアプリケーションをビルドするコンテナに設定します。
また、COPY命令の引数を見てみるとコピーしているのは/work/exec-webapp.shというシェルスクリプトのみで肝心のアプリケーションやその他の必要なファイルが一切含まれていないこと、シェルスクリプトに実行権限が与えられていないことがわかります。
以上を踏まえて修正を行うと以下のようになります。

FROM golang:alpine AS builder ADD ./Score /work WORKDIR /work RUN apk -U add git RUN go get -u github.com/go-sql-driver/mysql && \ go get -u github.com/go-gorp/gorp && \ go get -u github.com/gin-gonic/gin && \ go get -u github.com/garyburd/redigo/redis && \ go build -tags netgo -o webapp main.go FROM busybox ENV GOPATH=/usr/local/bin/ COPY --from=builder /work /usr/local/bin/ RUN chmod +x /usr/local/bin/exec-webapp.sh ENTRYPOINT ["/usr/local/bin/exec-webapp.sh", "/usr/local/bin/webapp"]

コンテナが起動しhttp://[問題サーバIP]:8080/indexでWebAppが起動していれば成功です。

解答Dockerfile一例

FROM golang:alpine AS builder ADD ./Score /work WORKDIR /work RUN apk -U add git RUN go get -u github.com/go-sql-driver/mysql && \ go get -u github.com/go-gorp/gorp && \ go get -u github.com/gin-gonic/gin && \ go get -u github.com/garyburd/redigo/redis && \ go build -tags netgo -o webapp main.go FROM busybox ENV GOPATH=/usr/local/bin/ COPY --from=builder /work /usr/local/bin/ RUN chmod +x /usr/local/bin/exec-webapp.sh ENTRYPOINT ["/usr/local/bin/exec-webapp.sh", "/usr/local/bin/webapp"]

採点基準

Golangの依存関係の解消 -> 50
中間イメージへの名前指定 -> 50
COPYのsource側ディレクトリ修正にてWebAppが起動 -> 100

合計200点